#include "defs.h"
#include "grtcirc.h"
+#include "queue.h"
+#include "src/core/datetime.h"
#include "src/core/file.h"
#include "src/core/xmlstreamwriter.h"
#include "src/core/xmltag.h"
#include "xmlgeneric.h"
-#include <QtCore/QRegExp>
-#include <QtCore/QXmlStreamAttributes>
+#include <QtCore/QXmlStreamAttributes> // for QXmlStreamAttributes
+#include <QtCore/QDateTime> // for QDateTime
+#include <QtCore/QFile> // for QFile
+#include <QtCore/QList> // for QList
+#include <QtCore/QString> // for QString, QStringLiteral, QStat...
+#include <QtCore/QtGlobal> // for qint64, qPrintable
+#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
+#include <cstring>
+#include <tuple>
// options
static char* opt_deficon = nullptr;
static route_head* gx_trk_head;
static QList<gpsbabel::DateTime>* gx_trk_times;
+static QList<std::tuple<int, double, double, double>>* gx_trk_coords;
static gpsbabel::File* oqfile;
static gpsbabel::XmlStreamWriter* writer;
{ gx_trk_e, cb_end, "/Placemark/*gx:Track" },
{ gx_trk_when, cb_cdata, "/Placemark/*gx:Track/when" },
{ gx_trk_coord, cb_cdata, "/Placemark/*gx:Track/gx:coord" },
+ { gx_trk_s, cb_start, "/Placemark/Track" }, // KML 2.3
+ { gx_trk_e, cb_end, "/Placemark/Track" }, // KML 2.3
+ { gx_trk_when, cb_cdata, "/Placemark/Track/when" }, // KML 2.3
+ { gx_trk_coord, cb_cdata, "/Placemark/Track/coord" }, // KML 2.3
+ { gx_trk_s, cb_start, "/Placemark/MultiTrack/Track" }, // KML 2.3
+ { gx_trk_e, cb_end, "/Placemark/MultiTrack/Track" }, // KML 2.3
+ { gx_trk_when, cb_cdata, "/Placemark/MultiTrack/Track/when" }, // KML 2.3
+ { gx_trk_coord, cb_cdata, "/Placemark/MultiTrack/Track/coord" }, // KML 2.3
{ nullptr, (xg_cb_type) 0, nullptr }
};
void wpt_ts_begin(xg_string args, const QXmlStreamAttributes*)
{
- wpt_timespan_begin = xml_parse_time(args);
+ wpt_timespan_begin = xml_parse_time(args);
}
void wpt_ts_end(xg_string args, const QXmlStreamAttributes*)
{
- wpt_timespan_end = xml_parse_time(args);
+ wpt_timespan_end = xml_parse_time(args);
}
void wpt_coord(const QString& args, const QXmlStreamAttributes*)
*
* If this is specified, then SetCreationDate
*/
- if ( wpt_timespan_begin.isValid() && wpt_timespan_end.isValid() ) {
-
- // If there are some Waypoints, then distribute the TimeSpan to all Waypoints
- if ( trk_head->rte_waypt_ct > 0 ) {
- qint64 timespan_ms = wpt_timespan_begin.msecsTo(wpt_timespan_end);
- qint64 ms_per_waypoint = timespan_ms / trk_head->rte_waypt_ct;
- queue* curr_elem;
- queue* tmp_elem;
- QUEUE_FOR_EACH(&trk_head->waypoint_list, curr_elem, tmp_elem) {
- trkpt = reinterpret_cast<Waypoint *>(curr_elem);
- trkpt->SetCreationTime(wpt_timespan_begin);
- wpt_timespan_begin = wpt_timespan_begin.addMSecs(ms_per_waypoint);
- }
- }
+ if (wpt_timespan_begin.isValid() && wpt_timespan_end.isValid()) {
+
+ // If there are some Waypoints, then distribute the TimeSpan to all Waypoints
+ if (trk_head->rte_waypt_ct > 0) {
+ qint64 timespan_ms = wpt_timespan_begin.msecsTo(wpt_timespan_end);
+ qint64 ms_per_waypoint = timespan_ms / trk_head->rte_waypt_ct;
+ queue* curr_elem;
+ queue* tmp_elem;
+ QUEUE_FOR_EACH(&trk_head->waypoint_list, curr_elem, tmp_elem) {
+ trkpt = reinterpret_cast<Waypoint*>(curr_elem);
+ trkpt->SetCreationTime(wpt_timespan_begin);
+ wpt_timespan_begin = wpt_timespan_begin.addMSecs(ms_per_waypoint);
+ }
+ }
}
}
track_add_head(gx_trk_head);
delete gx_trk_times;
gx_trk_times = new QList<gpsbabel::DateTime>;
+ delete gx_trk_coords;
+ gx_trk_coords = new QList<std::tuple<int, double, double, double>>;
}
void gx_trk_e(xg_string, const QXmlStreamAttributes*)
{
+ // Check that for every temporal value (kml:when) in a kml:Track there is a position (kml:coord) value.
+ // Check that for every temporal value (kml:when) in a gx:Track there is a position (gx:coord) value.
+ if (gx_trk_times->size() != gx_trk_coords->size()) {
+ fatal(MYNAME ": There were more coord elements than the number of when elements.\n");
+ }
+
+ // In KML 2.3 kml:Track elements kml:coord and kml:when elements are not required to be in any order.
+ // In gx:Track elements all kml:when elements are required to precede all gx:coord elements.
+ // For both we allow any order. Many writers using gx:Track elements don't adhere to the schema.
+ while (!gx_trk_times->isEmpty()) {
+ Waypoint* trkpt = new Waypoint;
+ trkpt->SetCreationTime(gx_trk_times->takeFirst());
+ double lat, lon, alt;
+ int n;
+ std::tie(n, lat, lon, alt) = gx_trk_coords->takeFirst();
+ // An empty kml:coord element is permitted to indicate missing position data;
+ // the estimated position may be determined using some interpolation method.
+ // However if we get one we will throw away the time as we don't have a location.
+ // It is not clear that coord elements without altitude are allowed, but our
+ // writer produces them.
+ if (n >= 2) {
+ trkpt->latitude = lat;
+ trkpt->longitude = lon;
+ if (n >= 3) {
+ trkpt->altitude = alt;
+ }
+ track_add_wpt(gx_trk_head, trkpt);
+ } else {
+ delete trkpt;
+ }
+ }
+
if (!gx_trk_head->rte_waypt_ct) {
track_del_head(gx_trk_head);
}
delete gx_trk_times;
gx_trk_times = nullptr;
+ delete gx_trk_coords;
+ gx_trk_coords = nullptr;
}
void gx_trk_when(xg_string args, const QXmlStreamAttributes*)
void gx_trk_coord(xg_string args, const QXmlStreamAttributes*)
{
- double lat, lon, alt;
-
- if (! gx_trk_times || gx_trk_times->isEmpty()) {
- fatal(MYNAME ": There were more gx:coord elements than the number of when elements.\n");
+ if (! gx_trk_coords) {
+ fatal(MYNAME ": gx_trk_coord: invalid kml file\n");
}
- Waypoint* trkpt = new Waypoint;
- trkpt->SetCreationTime(gx_trk_times->takeFirst());
+
+ double lat, lon, alt;
int n = sscanf(CSTR(args), "%lf %lf %lf", &lon, &lat, &alt);
- // Empty gx_coord elements are allowed to balance the number of when elements,
- // but if we get one we will throw away the time as we don't have a location.
- // It is not clear that coord elements without altitude are allowed, but our
- // writer produces them.
if (0 != n && 2 != n && 3 != n) {
- fatal(MYNAME ": gx:coord field decode failure on \"%s\".\n", qPrintable(args));
- }
- if (n >= 2) {
- trkpt->latitude = lat;
- trkpt->longitude = lon;
- if (n >= 3) {
- trkpt->altitude = alt;
- }
- track_add_wpt(gx_trk_head, trkpt);
- } else {
- delete trkpt;
+ fatal(MYNAME ": coord field decode failure on \"%s\".\n", qPrintable(args));
}
+ gx_trk_coords->append(std::make_tuple(n, lat, lon, alt));
}
static
queue* elem, *tmp;
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
int first_in_trk = tpt->Q.prev == &header->waypoint_list;
if (!first_in_trk && tpt->wpt_flags.new_trkseg) {
needs_multigeometry = 1;
}
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
int first_in_trk = tpt->Q.prev == &header->waypoint_list;
if (tpt->wpt_flags.new_trkseg) {
if (!first_in_trk) {
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* wpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* wpt = reinterpret_cast<Waypoint*>(elem);
switch (member) {
case fld_power:
queue* elem, *tmp;
int points_with_time = 0;
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
if (tpt->GetCreationTime().isValid()) {
points_with_time++;
queue* elem, *tmp;
kml_track_hdr(header);
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
kml_track_disp(tpt);
}
kml_track_tlr(header);
kml_output_positioning(false);
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
if (tpt->GetCreationTime().isValid()) {
QString time_string = tpt->CreationTimeXML();
// TODO: How to handle clamped, floating, extruded, etc.?
QUEUE_FOR_EACH(&header->waypoint_list, elem, tmp) {
- Waypoint* tpt = reinterpret_cast<Waypoint *>(elem);
+ Waypoint* tpt = reinterpret_cast<Waypoint*>(elem);
if (kml_altitude_known(tpt)) {
writer->writeTextElement(QStringLiteral("gx:coord"),
track points if we've not moved a minimum distance from the
beginning of our accumulated track. */
{
- Waypoint* newest_posn= reinterpret_cast<Waypoint *>QUEUE_LAST(&posn_trk_head->waypoint_list);
+ Waypoint* newest_posn= reinterpret_cast<Waypoint*>QUEUE_LAST(&posn_trk_head->waypoint_list);
if (radtometers(gcdist(RAD(wpt->latitude), RAD(wpt->longitude),
RAD(newest_posn->latitude), RAD(newest_posn->longitude))) > 50) {
*/
while (max_position_points &&
(posn_trk_head->rte_waypt_ct >= max_position_points)) {
- Waypoint* tonuke = reinterpret_cast<Waypoint *>QUEUE_FIRST(&posn_trk_head->waypoint_list);
+ Waypoint* tonuke = reinterpret_cast<Waypoint*>QUEUE_FIRST(&posn_trk_head->waypoint_list);
track_del_wpt(posn_trk_head, tonuke);
}